Basic CRUD
To create a model you need to create a model generator first. Typically your app would implement its own ModelGenerator class which extends from ReduxModelGenerator. If you want to know how that is done look at the models documentation.
In the following examples it is assumed that you have such a class in your project and it is called Generator.
EntityModel
EntityModel comes with the basic CRUD actions out of the box, which reduces the amount of boilerplate you have to write.
To create a new model all you need is a name and a type. The name is very important, since it will be used as the base to construct all request urls. The type will be used to type API responses/requests.
The name is the first parameter passed to the Generator constructor.
The 2nd parameter is the config, which add information about the includes that model has. An include is a field that is not returned by the API by default and is only present when you explicitly ask for it.
info
A new model needs to be registered to connect to the central redux store. If a model is not registered, none of the data will show up.
type Property = {
id: string;
address: string;
owner: Contact;
};
const config = {
entities: {
related: {
owner: {
include: 'owner'
}
}
}
};
export const propertiesModel = new Generator<Property>(
'properties',
config
).createEntityModel();
Data fetching
List
To load a list of records we need to define a query, which specifies the fields we require. We can use the hook useEntityListQuery to fetch the query for us.
const propertiesQuery = query`{
${propertiesModel} {
address
owner
}
}`;
function ListOfProperties() {
const { status, data } = useEntityListQuery(propertiesQuery);
if (status === 'loading') return <LoadingSpinner />;
return (
<ul>
{data.map((property) => (
<li>
{property.address} - {property.owner.name}
</li>
))}
</ul>
);
}
Single record
The approach to fetching a single record is very similar to fetching a list. All the query needs is the ID of the record you are trying to fetch. Because of the ID, the resulting query object will no longer be static and would change between renders. To avoid fetching the same query multiple times, we need to pass the same reference to the hook. To achieve this we can use memoisation using the useMemo hook.
const getPropertyQuery = (id: string) => query`{
${propertiesModel} (id: ${id}) {
address
owner
}
}`;
function PropertyDetails({ propertyId }: PropertyDetailsProps) {
const propertyQuery = useMemo(() => getPropertyQuery(propertyId), [
propertyId
]);
const { status, data } = useEntityQuery(propertyQuery);
if (status === 'loading') return <LoadingSpinner />;
return (
<>
<h1>{data.address}</h1>
<section>Owner: {data.owner.name}</section>
</>
);
}
Manipulating records
Creating records
Basic CRUD functionality is provided by model generator out of the box. These actions are available from both the useEntityQuery as well as the useEntityListQuery hooks. Alternatively you can simply use the useModelActions hook, which will return all available actions for a model. To create a new record, simply destruct the createItem function from the returned actions.
function CreateNewProperty() {
const { createItem } = useModelActions(propertiesModel);
return (
<CreatePropertyForm
onSubmit={(property) => createItem({ data: property })}
/>
);
}
Updating records
Use the provided action updateItem. You will need an ID and the values for the record. Whether this action uses PUT and only requires a subset of the record's properties, or POST and requires the entire record, is up to the implementation of both the backend that is being connected to and the implementation within the model generator configuration.
function EditPropertyButton() {
const { updateItem } = useModelActions(propertiesModel);
const { open: openEditPropertyModal } = useDialog(EditPropertyDialog);
return (
<button
onClick={() =>
openEditPropertyModal({
onSubmit: (updatedProperty) => updateItem({ data: updatedProperty })
})
}
>
Edit
</button>
);
}
bulkUpdateItems
caution
Using bulkUpdateItems is discouraged at this point since the implementation is not stable
Deleting records
trashItem
The action trashItem can be used to delete a record just using their ID.
deleteItem
Another action to delete items is deleteItem. It was supposed to be used in cases the API allowed for hard and soft deletes.
It's usage depends on the application config of model generator.
Best practice is to use trashItem for now.
function DeletePropertyButton({
propertyId,
...props
}: DeletePropertyButtonProps) {
const { deleteItem } = useModelActions(propertiesModel);
return (
<button {...props} onClick={() => deleteItem({ id: propertyId })}></button>
);
}
bulkTrashItems
caution
Using bulkTrashItems is discouraged at this point since the implementation is not stable